<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
	<style>
		.ab{
			font-size: 18px;
		}
	</style>
</head>
<body>
<div id="d1">100</div>
<div id="d2">200</div>
<div id="d3">300</div>
<div id="d4">400</div>
<script>
	//$("div").css('color','red')
	//1.独立的单元
	//  -->

	// 1、获取样式不能使用 xxx.style.color 因为只能获取行内样式
	// 2、获取样式的正确方式：
	//    -->现代浏览器：window.getComputedStyle(dom,null)
	//    -->IE6-8：dom.currentStyle
(function (global) {

	function jQuery(selector) {
		//1.获取页面中所有的元素
		//2.把这个元素放到一个特定的对象中
		return new jQuery.fn.init(selector);
	}

	//jquery对象的构造函数

	jQuery.fn = jQuery.prototype = {
		constructor: jQuery,
	    init: function(selector) {
			if(jQuery.type(selector) ==='string'){
				// 选择器
				//jquery内容封装了一个Sizzle引擎来获取DOM元素

				//把DOM放到这个对象中
				const elements = document.querySelectorAll(selector);
				//为了让这些元素可以在css方法中访问，所以要把这些元素放在对象上面进行传递
				//this.elements = elements;
				// -->jquery为了后续DOM操作的方便，奖这些获取到的DOM元素全部放在了对象自己身上，让自己变成一个就像数组一样，可以使用for循环进行遍历，我们把这种对象特性称之为[伪数组]

				//把这个所有DOM元素都添加到自己身上
				for(let i=0;i<elements.length;i++){
					// ele: DOM元素
					this[i] = elements[i];
				}
				this.length = elements.length;
			}else if(selector.nodeType){
				// DOM元素
			    this[0] = selector
			    this.length = 1;
			}
	    }
	}

	jQuery.fn.init.prototype = jQuery.fn;

	jQuery.fn.extend = jQuery.extend = function (...args) {
	  //2个extend方法区别在于：
	  //   -->$.fn.extend：所有的参数

	  // 后面拷贝过程都是一样的

	  let target,source = []

	      source = [...args]

	  //判断2种情况
	  // $.extend({}) -->给$添加属性
	  // $.fn.extend({}) -->给$.fn添加属性

	  // 参数个数>1，就是给第一个实参添加属性
	  if(args.length === 1){
	    target = this
	  }else {
	  	// 参数个数>1
		target = args[0] //目标对晚服就是第一个实参
	    source.splice(0,1) //源对象就是第二个及其后面的实参
	  }

	  /*source.forEach((item)=>{
	  	// item:就是第一个数据源对象（提供数据的对象）
	    Object.assign(target,item);
	  })*/

	  // a.Object.assign其实是支持多参数
	  // b.优化思路：Object.assign(target,source[0],source[1],source[2]...)
	  //    --> Object.assign(target,...source)
	  Object.assign(target,...source)

	  return target


	  /*

	  //这里extend方法参数并不确定
	  //所以建议不要指定形参，通过函数内置对象arguments操作
	  //console.log(args);

	  var target = args[0];

	  //进行对象拷贝，需要将第二个参数及其以后的所有参数的属性遍历添加到第一个参数中
	  for(let i=0;i<args.length;i++){
	  	//每一个实参：对象
	  	let arg = args[i]
	    //取出对象中的每一个属性
	    for(let key in arg){
	  		//取出属性值
	  		// let value = arg[key];
	  		//把该属性添加到第一个参数中
		    target[key] = arg[key];
	    }
	  };

	  return target;
	  */
	}

	// 添加一些工具类方法：
        // -->$.ajax  $.post $.get $.type　$.each
        // -->这里方法不到$,只能访问jQuery
    jQuery.extend({
        // $.each([1,3,5],function(index,value){})
	    // $.each({age:18,height:185},function(key,value){})
        // $.each()可以遍历数组和对象
        each(obj,callback) {
	        // 有2种情况，数组用for循环，对象用for..in循环
            // Object.prototype.toString.call(obj) === "[object Array]"
            // -->不仅仅可以遍历级数，也可以遍历伪数组{0:100,length:1}
            // 在这里由于存在数组、伪数组2种情况，只能使用一种约定俗成的方式来通过他们的特征来判断：length属性，并且值>=0
            if((length in obj) && obj.length >= 0 ) {
            	//obj里有length并且obj.length>=0
                for(let i=0;i<obj.length;i++){
	                // callback(i, obj[i])
	                callback.call(obj[i],i,obj[i])
                    // callback.apply(obj[i],[i,obj[i]])
                }
            }else {
            	for(let i in obj){
		            // callback(i, obj[i])
		            callback.call(obj[i],i,obj[i])
		            // callback.apply(obj[i],[i,obj[i]])
	            }
            }
        },
        type(data) {
        	// 判断data的数据类型
            let type = Object.prototype.toString.call(data)
            return type
                    .replace("[object ","")
                    .replace("]","")
                    .toLowerCase();
        }
    })

    //添加dom方法
    jQuery.fn.extend({
      each(callback) {
      	jQuery.each(this,callback)

        return this;
      }
    })

    //　样式操作部分


	// 将会保存曾经绑定过的所有事件处理函数
    // 以DOM元素为区分，
	const events = [
		// {ele:div1, type:'click', callback:function(){} },
		// {ele:div1, type:'click', callback:function(){} },
		// {ele:div1, type:'keydown', callback:function(){} },
		// {ele:div3, type:'click', callback:function(){} },
	];
	jQuery.fn.extend({
      // 1.获取样式$('div').css("color") 只能获取到第一个div的颜色
      // 2.设置样式
      //    -->$("div").css("color","red") 设置每一个div的字体颜色
      //    -->$("div").css({color:'red',backgroundColor:'blue'})
      css(...args) {
        var arg1 = args[0],
	        arg2 = args[1];
        //参数个数：1
        if(args.length === 1){
            if(jQuery.type(arg1) === "string"){
	            // a.获取样式: 只能获取第一个元素的样式
				let firstDom = this[0]

                // 错误写法
                // return firstDom.style[arg1] //只能获取行内样式

                // 正确写法
                let domStyleObj = window.getComputedStyle(firstDom, null)
                return domStyleObj[arg1];
            }else {
	            // b.设置多个样式 arg1:{color:'red',fontSize:'28px'}
				/*this.each(function () {
				  // this:DOM元素
				  /!*
				  let _that = this
				  jQuery.each(arg1,function (key,val) {
					  _that.style[key] = val
				  })
				  *!/
					jQuery.each(arg1,(key,val) => {
						this.style[key] = val
					})
				})*/
                var _that = this;
                // 遍历出所有要添加的样式
	            jQuery.each(arg1,function(key,val) {
	                // 遍历每个DOM元素，添加指定的样式
		            _that.css(key,val)
	            })

                return _that;// 实现链式编程
            }
        }else {
	        //参数个数大于：2 设置单个样式

            // 第一步：遍历每一个DOM
            // 第二步：给DOM添加样式
            // this：表示jquery对象

            // this.each(function(index,dom){
            // 	//this: 表示一个DOM对象 === dom
            //     this.style[arg1] = arg2
            // })
            // return this;

            //等价于：
            return this.each(function(index,dom){
	            //this: 表示一个DOM对象 === dom
	            this.style[arg1] = arg2
            })
        }

      },
      show() {
		  // 让所有的元素显示出来
          this.css("display","block");
          return this;
      },
      hide() {
	      this.css("display","none");
	      return this;
      },
      toggle() {
        //如果隐藏则显示，如果显示则隐藏
        this.each(function () {
          //this:元素 //不能使用this.css('display')，访问不到
          // if(window.getComputedStyle(this)['display'] === "none"){
          //
          // }

          // 解决方案：jquery提供了通过某种途径将dom元素转换为jquery对象（init里判断）
          // if(jQuery(this).css('display') === 'none'){
	       //    jQuery(this).show();
          // }else {
	       //    jQuery(this).hide();
          // }

          // 问题：jquery(this)都会产生一个新的jquery对象
          // 而每一次产生一个新的jquery对象都会开辟一块新的内存
          // 而这里的dom元素是唯一的，所以导致了一些不必要的内存浪费
          //   jQuery(this).css('display') === 'none' ?
          //     jQuery(this).show() :
          //     jQuery(this).hide();
          // 解决方案1：
          // let $this = jQuery(this);
          //     $this.css('display') === 'none' ?
			//         $this.show() :
			//         $this.hide();
          // 解决方案2：
	      let $this = jQuery(this);
	          $this[$this.css('display') === 'none' ? 'show' : 'hide']();
        })
      },
      on(type, callback) {
        // $('div').on('click',function(){})
        // 给当前jquery对象中的每一个DOM元素绑定事件
        this.each(function (index, element) {
	        element.addEventListener(type, callback)//callback.bind(绑定this)

            events.push({ele:element,type,callback})
        })
        // 实现链式编程
        return this;
      },
      // 解除绑定：$('div').off('click'):表示解除当前元素所有的单击事件
      off(type){
		this.each(function (index,element) {
			//遇到问题：并不能得知之前绑定事件的架设函数的地址
		    // 解决方案：必须在当前绑定事件的时候，把事件回调函数的内存地址保存下来
			// element.removeEventListener(type, )

		    // 找到该幸免于难曾经绑定过type类型的事件
		    let evts = events.filter(function (evtObj) {
		        // 是否是该元素绑定该类型的事件
				let isCurrent = evtObj.ele === element &&
								evtObj.type === type
		        return isCurrent;
			})

		    // 进行事件的解绑
		    evts.forEach(function (evt) {
		      var { callback } = evt;   //callback = evt.callback;
			  element.removeEventListener(type,callback);
		    })
		})
      }
    })


	global.$=global.jQuery = jQuery;


})(window)
/*$.each({5:100,1:200,2:300,length:4},function(index,val){
	console.log(this)
	console.log(index,val)
});*/
/*$('div').each(function(index,ele){
	console.log(index,ele)
})*/
$('#d1').on("click",function () {
  console.log(11)
})
$('#d1').on("click",function () {
	console.log(22)
})
$('#d1').on("mouseover",function () {
	console.log(333)
})
$('#d3').on("click",function () {
	console.log('d3')
})
$('#d1').off("click");
</script>
</body>
</html>